<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   
      <title>5.3. ORM</title>
      <meta name="generator" content="DocBook XSL-NS Stylesheets V1.75.2">
      <link rel="home" href="indice.html" title="Manual Usuario gvHidra">
      <link rel="up" href="ch05.html" title="Cap&iacute;tulo 5. Fuentes de datos">
      <link rel="prev" href="ch05s02.html" title="5.2. Web Services">
      <link rel="next" href="ch06.html" title="Cap&iacute;tulo 6. Seguridad">
   </head>
   <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
      <div class="navheader">
         <table width="100%" summary="Navigation header">
            <tr>
               <th colspan="3" align="center">5.3. ORM</th>
            </tr>
            <tr>
               <td width="20%" align="left"><a accesskey="p" href="ch05s02.html">Anterior</a>&nbsp;
               </td>
               <th width="60%" align="center">Cap&iacute;tulo 5. Fuentes de datos</th>
               <td width="20%" align="right">&nbsp;<a accesskey="n" href="ch06.html">Siguiente</a></td>
            </tr>
         </table>
         <hr>
      </div>
      <div class="section" title="5.3. ORM">
         <div class="titlepage">
            <div>
               <div>
                  <h2 class="title" style="clear: both"><a name="d4e4202"></a>5.3. ORM
                  </h2>
               </div>
            </div>
         </div>
             
         
             
         <p>En este apartado vamos a hablar de como integrar un ORM en el
            framework gvHIDRA. Actualmente, no se ha desarrollado una clase espec&iacute;fica
            para uso de ORMs, pero es, como veremos en este apartado, sencilla su
                integraci&oacute;n.
         </p>
         
             
         <p></p>
         
             
         <div class="section" title="5.3.1. Introducci&oacute;n">
            <div class="titlepage">
               <div>
                  <div>
                     <h3 class="title"><a name="d4e4206"></a>5.3.1. Introducci&oacute;n
                     </h3>
                  </div>
               </div>
            </div>
                  
            
                  
            <p>Entendemos un ORM (mapeo objeto-relacional) como un sistema o
               t&eacute;cnica de programaci&oacute;n para convertir datos entre el sistema de tipos
               utilizado en un lenguaje de programaci&oacute;n orientado a objetos y el
                     utilizado en una base de datos relacional. En la pr&aacute;ctica esto crea una
                     base de datos orientada a objetos virtual, sobre la base de datos
                     relacional. Esto posibilita el uso de las caracter&iacute;sticas propias de la
                     orientaci&oacute;n a objetos (b&aacute;sicamente herencia y polimorfismo).
            </p>
            
                  
            <p>Utilizar un ORM tiene una serie de ventajas que nos facilitan
                     enormemente tareas comunes y de mantenimiento:
            </p>
            
                  
            <div class="itemizedlist">
               <ul class="itemizedlist" type="disc">
                  <li class="listitem">
                                 
                     <p><span class="bold"><strong>Reutilizaci&oacute;n</strong></span>: La principal
                                    ventaja que aporta un ORM es la reutilizaci&oacute;n permitiendo llamar a
                                    los m&eacute;todos de un objeto de datos desde distintas partes de la
                                    aplicaci&oacute;n e incluso desde diferentes aplicaciones.
                     </p>
                               
                  </li>
                  <li class="listitem">
                                 
                     <p><span class="bold"><strong>Encapsulaci&oacute;n</strong></span>: La capa ORM
                                    encapsula la l&oacute;gica de los datos pudiendo hacer cambios que
                                    afectan a toda la aplicaci&oacute;n &uacute;nicamente modificando una
                                    funci&oacute;n.
                     </p>
                               
                  </li>
                  <li class="listitem">
                                 
                     <p><span class="bold"><strong>Portabilidad</strong></span>: Utilizar una
                                    capa de abstracci&oacute;n nos permite cambiar en mitad de un proyecto de
                                    una base de datos MySQL a una Oracle sin ning&uacute;n tipo de
                                    complicaci&oacute;n. Esto es debido a que no utilizamos una sintaxis
                                    MySQL, Oracle o SQLite para acceder a nuestro modelo, sino una
                                    sintaxis propia del ORM utilizado que es capaz de traducir a
                                    diferentes tipos de bases de datos.
                     </p>
                               
                  </li>
                  <li class="listitem">
                                 
                     <p><span class="bold"><strong>Seguridad</strong></span>: Los ORM suelen
                                    implementar mecanismos de seguridad que protegen nuestra
                                    aplicaci&oacute;n de los ataques m&aacute;s comunes como SQL Injections.
                     </p>
                               
                  </li>
                  <li class="listitem">
                                 
                     <p><span class="bold"><strong>Mantenimiento del c&oacute;digo</strong></span>:
                                    Gracias a la correcta ordenaci&oacute;n de la capa de datos, modificar y
                                    mantener nuestro c&oacute;digo es una tarea sencilla.
                     </p>
                               
                  </li>
               </ul>
            </div>
                
         </div>
         
             
         <div class="section" title="5.3.2. Ejemplo: Propel ORM">
            <div class="titlepage">
               <div>
                  <div>
                     <h3 class="title"><a name="d4e4227"></a>5.3.2. Ejemplo: Propel ORM
                     </h3>
                  </div>
               </div>
            </div>
                  
            
                  
            <p>Actualmente existen muchas soluciones ORM con PHP en el mercado,
                     tanto libres como propietario, aunque para este ejemplo hemos optado por
                     un enfoque Open-Source con el objetivo de seguir con la misma direcci&oacute;n
                     que gvHidra. Como se ha indicado anteriormente, cualquier otra soluci&oacute;n
                     ser&iacute;a v&aacute;lida.
            </p>
            
                  
            <div class="mediaobject"><img src="images/propel-logo.png"></div>
            
                  
            <p>Propel es un ORM basado en PHP. Mediante el mapeo de una BBDD en
                     XML se generan un conjunto de clases para su manejo, simplificando de
                     este modo el acceso a los datos, dando una soluci&oacute;n CRUD.
            </p>
            
                  
            <div class="section" title="5.3.2.1. Caracter&iacute;sticas">
               <div class="titlepage">
                  <div>
                     <div>
                        <h4 class="title"><a name="d4e4234"></a>5.3.2.1. Caracter&iacute;sticas
                        </h4>
                     </div>
                  </div>
               </div>
                       
               
                       
               <p>Seg&uacute;n indica la propia documentaci&oacute;n oficial de Propel, funciona
                          con los siguientes SGBD&#8217;s:
               </p>
               
                       
               <div class="itemizedlist">
                  <ul class="itemizedlist" type="disc">
                     <li class="listitem">
                                      
                        <p>PostgreSQL</p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>MySQL</p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Oracle</p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Sql Server</p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Sqlite</p>
                                    
                     </li>
                  </ul>
               </div>
               
                       
               <p>El trabajo con las clases generadas es c&oacute;modo e intuitivo
                          siempre que se tengan nociones de desarrollo con objetos.En un
                          principio aunque la curva de aprendizaje puede parecer mas compleja de
                          lo esperado, por las pruebas realizadas el resultado mediante este ORM
                          es r&aacute;pido y eficaz. Realmente no cuesta un tiempo excesivo hacer una
                          consulta o inserci&oacute;n en las tablas de la BBDD. Las opciones que nos
                          brinda este ORM son muchas y variadas; van desde realizar consultas
                          sin necesidad de utilizar SQL, hasta controlar la consulta que se va a
                          realizar contra la BD introduciendo una sentencia SQL mas
                          compleja.
               </p>
               
                       
               <p>El tratamiento de los datos devueltos en las consultas tambi&eacute;n
                          es un punto importante, ya que se pueden recupera un solo elemento de
                          una consulta, o en caso de necesitarlo, un array de objetos que
                          contienen esa informaci&oacute;n. Eso significa que de una sola consulta, el
                          ORM nos permite recuperar un conjunto finito de resultados, un
                          conjunto finito de objetos de una clase.
               </p>
               
                       
               <p>Propel nos da una enorme variedad de opciones, estas nos
                          cubrir&aacute;n en la mayor&iacute;a de los casos aquellas funciones o necesidades
                          que tengamos. Gracias a la herencia, si alguno de los casos no
                          estuviera cubierto como deber&iacute;a, entonces tenemos la opci&oacute;n de
                          crearnos nuestra propia clase de gesti&oacute;n de los datos, con las mejoras
                          que consideremos oportunas. Recordemos que disponemos de todas las
                          ventajas de la programaci&oacute;n orientada a objetos, como la sobrecarga o
                          la herencia.
               </p>
               
                       
               <p>Tambi&eacute;n nos da una gran variedad de operaciones, aparte de las
                          b&aacute;sicas de CRUD. A continuaci&oacute;n se detallan algunas de las m&aacute;s
                          importantes:
               </p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                      
                        <p><span class="emphasis"><em>Inserci&oacute;n de filas
                                               relacionadas</em></span></p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p><span class="emphasis"><em>Guardado en cascada</em></span></p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p><span class="emphasis"><em>Uso de las relaciones en una
                                               consulta</em></span></p>
                        
                                      
                        <div class="itemizedlist">
                           <ul class="itemizedlist" type="disc">
                              <li class="listitem">
                                                     
                                 <p>B&uacute;squeda de registros relacionados con otro</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Consultas embebidas</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Relaciones uno a uno</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Relaciones muchos a muchos</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Reducir al m&iacute;nimo las consultas</p>
                                                   
                              </li>
                           </ul>
                        </div>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p><span class="emphasis"><em>Validaciones</em></span></p>
                        
                                      
                        <div class="itemizedlist">
                           <ul class="itemizedlist" type="disc">
                              <li class="listitem">
                                                     
                                 <p>Igual que</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>No igual que</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Tama&ntilde;o m&aacute;ximo</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Tama&ntilde;o m&iacute;nimo</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Tipo</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Valor v&aacute;lido</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>Personalizadas</p>
                                                   
                              </li>
                           </ul>
                        </div>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p><span class="emphasis"><em>Transacciones</em></span>: Las transacciones de
                                         bases de datos son la clave para asegurar la integridad de los
                                         datos y el rendimiento de las consultas de base de datos.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p><span class="emphasis"><em>Herencia</em></span></p>
                        
                                      
                        <div class="itemizedlist">
                           <ul class="itemizedlist" type="disc">
                              <li class="listitem">
                                                     
                                 <p>Igual que</p>
                                                   
                              </li>
                              <li class="listitem">
                                                     
                                 <p>No igual que</p>
                                                   
                              </li>
                           </ul>
                        </div>
                                    
                     </li>
                  </ol>
               </div>
                     
            </div>
            
                  
            <div class="section" title="5.3.2.2. Configuraci&oacute;n">
               <div class="titlepage">
                  <div>
                     <div>
                        <h4 class="title"><a name="d4e4307"></a>5.3.2.2. Configuraci&oacute;n
                        </h4>
                     </div>
                  </div>
               </div>
                       
               
                       
               <p>La instalaci&oacute;n se puede hacer mediante <span class="bold"><strong>PEAR</strong></span>, es importante tener en cuenta que
                          tendremos que haber instalado previamente tambi&eacute;n Phing, una vez hecho
                          esto, se accede a los canales correspondientes y se instalan los
                          paquetes de Propel.
               </p>
               
                       
               <p>La fase previa al uso de Propel en la cual se definen las
                          estructuras de datos y las clases manejadoras, se puede abordar de dos
                          modos:
               </p>
               
                       
               <div class="section" title="5.3.2.2.1. Desde un XML">
                  <div class="titlepage">
                     <div>
                        <div>
                           <h5 class="title"><a name="d4e4312"></a>5.3.2.2.1. Desde un XML
                           </h5>
                        </div>
                     </div>
                  </div>
                            
                  
                            
                  <p>Se generan a trav&eacute;s de funciones nativas de Propel y mas
                               concretamente &#8220;<span class="emphasis"><em>propel-gen</em></span>&#8221;, una serie de clases
                               manejadoras de la BBDD, tanto las clases base, como unas clases que
                               heredan de estas primeras
                  </p>
                  
                            
                  <p>Tendremos entonces que generar el SQL para poder crear las
                               tablas en la BBDD, este proceso tambi&eacute;n es automatizado mediante la
                               herramienta &#8220;propel-gen&#8221;, que nos generar&aacute; un fichero SQL
                               conteniendo toda la estructura de la BBDD, este fichero lo usaremos
                               para crear en la BBDD las tablas correspondientes.
                  </p>
                          
               </div>
               
                       
               <div class="section" title="5.3.2.2.2. Mediante Ingenier&iacute;a InversaL">
                  <div class="titlepage">
                     <div>
                        <div>
                           <h5 class="title"><a name="d4e4317"></a>5.3.2.2.2. Mediante Ingenier&iacute;a InversaL
                           </h5>
                        </div>
                     </div>
                  </div>
                            
                  
                            
                  <p>Obtendremos el XML con toda la estructura de las tablas
                               implicadas.
                  </p>
                  
                            
                  <p>Realizamos entonces los mismos pasos que en el primer caso,
                               para poder generar la estructura de las clases manejadoras.
                  </p>
                          
               </div>
                     
            </div>
            
                  
            <div class="section" title="5.3.2.3. En marcha">
               <div class="titlepage">
                  <div>
                     <div>
                        <h4 class="title"><a name="d4e4321"></a>5.3.2.3. En marcha
                        </h4>
                     </div>
                  </div>
               </div>
                       
               
                       
               <p>La instalaci&oacute;n se puede hacer mediante <span class="bold"><strong>PEAR</strong></span>, es importante tener en cuenta que
                          tendremos que haber instalado previamente tambi&eacute;n Phing, una vez hecho
                          esto, se accede a los canales correspondientes y se instalan los
                          paquetes de propel.
               </p>
               
                       
               <p>Pasos para realizar esta instalaci&oacute;n:</p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                      
                        <p>Se debe agregar el canal pear.propelorm.org al entorno de
                                         PEAR.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Una vez que tenemos el canal, puede instalar el paquete
                                         del generador, o el paquete de tiempo de ejecuci&oacute;n, o ambas
                                         cosas, si es una primera instalaci&oacute;n, preferentemente instalar
                                         ambas cosas.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Es importante hacer uso de la opci&oacute;n '-a' para permitir
                                         que PEAR pueda descargar e instalar las dependencias.
                        </p>
                                    
                     </li>
                  </ol>
               </div>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">pear channel-discover pear.propelorm.org
pear install -a propel/propel_generator
pear install -a propel/propel_runtime</pre>
                          </div>
               
                       
               <p>El generador de Propel utiliza Phing 2.3.3, si no esta
                          instalado, se debe instalar.
               </p>
               
                       
               <p>Pasos para realizar esta instalaci&oacute;n:</p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                      
                        <p>Se debe agregar el canal
                                         <span class="emphasis"><em>pear.phing.org</em></span> al entorno de PEAR.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Posteriormente instalamos el <span class="emphasis"><em>Phing</em></span> y
                                         el <span class="emphasis"><em>Log</em></span>.
                        </p>
                                    
                     </li>
                  </ol>
               </div>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">pear channel-discover pear.phing.info
pear install phing/phing
pear install Log</pre>
                          </div>
               
                       
               <p>Una vez realizadas las instalaciones, simplemente tendremos que
                          hacer uso de &#8220;propel-gen&#8221; para comprobar que todo ha funcionado
                          correctamente, aunque el proceso no realice ninguna funci&oacute;n, ya que no
                          habremos creado los ficheros necesarios para ello.
               </p>
               
                       
               <p>Podemos recuperar la informaci&oacute;n, los datos, de la base de datos
                          y prepararla en un fichero XML, para ello Propel tiene herramientas
                          que nos lo permitir&aacute;n, gener&aacute;ndonos un fichero XML que contiene todos
                          estos datos.
               </p>
               
                       
               <p>Posteriormente se explicaran los pasos previos a seguir para
                          hacer uso de Propel. Antes de poder comenzar a usar Propel, tendremos
                          que preparar la capa de comunicaci&oacute;n con nuestra BBDD para lo cual nos
                          vamos a encontrar con dos situaciones:
               </p>
               
                       
               <p><span class="bold"><strong>CASO 1</strong></span>: Partimos de una BBDD en
                          formato XML.
               </p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                      
                        <p>Se generan a trav&eacute;s de funciones nativas de Propel y mas
                                         concretamente &#8220;propel-gen&#8221;, una serie de clases manejadoras de
                                         la BBDD, tanto las clases base, como unas clases que heredan de
                                         estas primeras.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Tendremos entonces que generar el SQL para poder crear las
                                         tablas en la BBDD, este proceso tambi&eacute;n es automatizado mediante
                                         la herramienta &#8220;propel-gen&#8221;, que nos generar&aacute; un fichero SQL
                                         conteniendo toda la estructura de la BBDD, este fichero lo
                                         usaremos para crear en la BBDD las tablas
                                         correspondientes.
                        </p>
                                    
                     </li>
                  </ol>
               </div>
               
                       
               <p><span class="bold"><strong>CASO 2</strong></span>: Partimos de una BBDD ya
                          creada, usamos ingenier&iacute;a inversa.
               </p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                      
                        <p>Obtendremos el XML con toda la estructura de las tablas
                                         implicadas.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>a.Realizamos entonces los mismos pasos que en el primer
                                         caso, para poder generar la estructura de las clases
                                         manejadoras.
                        </p>
                                    
                     </li>
                  </ol>
               </div>
               
                       
               <p>Se detalla a nivel t&eacute;cnico cada uno de los dos casos, con su
                          comportamiento correspondiente.
               </p>
               
                       
               <p><span class="bold"><strong>CASO 1: BBDD en formato
                                XML</strong></span></p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                      
                        <p>Crearemos un nuevo fichero llamado
                                         <span class="emphasis"><em>schema.xml</em></span> que contendr&aacute; la estructura de
                                         nuestra BBDD siguiendo el siguiente esqueleto:
                        </p>
                        
                                      
                        <div class="informalexample">
                                           
                           
                                           <pre class="programlisting">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
 &lt;database name="" defaultIdMethod="native"&gt;
   &lt;table name="" phpName=""&gt;
      &lt;!-- column and foreign key definitions go here --&gt;
      &lt;column name="" type="integer" required="true" primaryKey="true" autoIncrement="true"/&gt;
       ... 
      &lt;foreign-key foreignTable="" phpName="" refPhpName=""&gt;
        &lt;reference local="" foreign=""/&gt;
      &lt;/foreign-key&gt;
     &#8230;
   &lt;/table&gt;
 &lt;/database&gt;</pre>
                                         </div>
                        
                                      
                        <p>En este fichero XML vamos a incluir toda la estructura de
                                         la BBDD con la que queremos trabajar, las columnas y las claves
                                         implicadas. Estos datos ser&aacute;n los que emplearemos para crear las
                                         tablas en nuestro SGBD.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Para la construcci&oacute;n final del modelo, necesitamos tanteo
                                         el reci&eacute;n creado schema.xml como un nuevo fichero
                                         build.properties que contendr&aacute; la informaci&oacute;n de la
                                         configuraci&oacute;n de la BBDD.
                        </p>
                        
                                      
                        <div class="informalexample">
                                           
                           
                                           <pre class="programlisting"># Driver de la BBDD
propel.database = pgsql|mysql|sqlite|mssql|oracle

# Nombre del proyecto
propel.project = &lt;&#8230;&gt;  </pre>
                                         </div>
                        
                                      
                        <p>Se ha de tener en cuenta que para el correcto
                                         funcionamiento, el nombre del proyecto debe ser el mismo que el
                                         de la BBDD.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Hemos de dejar estos dos ficheros schema.xml y
                                         build.properties en el mismo nivel dentro del subdirectorio del
                                         proyecto, solo nos queda hacer la llamada a Propel para que nos
                                         genere las clases correspondientes, esto es pas&aacute;ndole el
                                         parametro &#8220;om&#8221;, Object Model generador.
                        </p>
                        
                                      
                        <div class="informalexample">
                                           
                           
                                           <pre class="programlisting">Propel-gen om   </pre>
                                         </div>
                        
                                      
                        <p>Por cada tabla en la BBDD, Propel crea tres clases de
                                         PHP:
                        </p>
                        
                                      
                        <div class="itemizedlist">
                           <ul class="itemizedlist" type="disc">
                              <li class="listitem">
                                                   
                                 <p>Una clase modelo, lo que representa una fila en la
                                                      base de datos.
                                 </p>
                                                 
                              </li>
                              <li class="listitem">
                                                   
                                 <p>Una clase Peer, ofreciendo constantes est&aacute;ticas y
                                                      m&eacute;todos sobre todo por compatibilidad con versiones
                                                      anteriores de Propel.
                                 </p>
                                                 
                              </li>
                              <li class="listitem">
                                                   
                                 <p>Una clase Consulta que sirve para trabajar sobre una
                                                      tabla para poder recuperar y actualizar filas.
                                 </p>
                                                 
                              </li>
                           </ul>
                        </div>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Propel adem&aacute;s tambi&eacute;n puede generarnos el fichero con el
                                         c&oacute;digo SQL completo de nuestras tablas, para ello, el par&aacute;metro
                                         de llamada debe ser &#8220;sql&#8221;, no es obligatorio para el
                                         funcionamiento de Propel pero puede ser interesante para tener
                                         el modelo de datos y poder llevarlo a otro SGBD si lo
                                         consideramos necesario.
                        </p>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Ajustes de Conexi&oacute;n del Runtime. Ahora se debe agregar un
                                         archivo de configuraci&oacute;n para que las clases de objetos
                                         generadas y el runtime de Propel se puedan conectar a la base de
                                         datos, y registrar la actividad de Propel. Para ello crearemos
                                         un fichero con el nombre runtime-conf.xml que mantendr&aacute; el
                                         siguiente esqueleto, el ejemplo define una conexi&oacute;n a
                                         MySQL:
                        </p>
                        
                                      
                        <div class="informalexample">
                                           
                           
                                           <pre class="programlisting">&lt;?xml version=&#8221;1.0&#8221; encoding=&#8221;UTF-8&#8221;?&gt;
&lt;config&gt;
  &lt;!&#8212;Uncomment this if you have PEAR Log installed
  &lt;log&gt;
    &lt;type&gt;file&lt;/type&gt;
    &lt;name&gt;/path/to/propel.log&lt;/name&gt;
    &lt;ident&gt;propel-bookstore&lt;/ident&gt;
    &lt;level&gt;7&lt;/level&gt;
  &lt;/log&gt;
  
  &lt;propel&gt;
    &lt;datasources default=&#8221;&#8221;&gt;
      &lt;datasource id=&#8221;&#8221;&gt;
        &lt;adapter&gt;mysql&lt;/adapter&gt;
        &lt;connection&gt;
          &lt;dsn&gt;mysql:host=localhost;dbname=&lt;/dsn&gt;
          &lt;user&gt;&lt;/user&gt;
          &lt;password&gt;&lt;/password&gt;
        &lt;/connection&gt;
      &lt;/datasource&gt;
    &lt;/datasources&gt;
  &lt;/propel&gt;
&lt;/config&gt;</pre>
                                         </div>
                                    
                     </li>
                     <li class="listitem">
                                      
                        <p>Ahora creamos la configuraci&oacute;n para el Runtime mediante la
                                         llamada a
                        </p>
                        
                                      
                        <div class="informalexample">
                                           
                           
                                           <pre class="programlisting">propel-gen convert-conf.</pre>
                                         </div>
                        
                                      
                        <p>Esto nos genera en el directorio \conf de nuestra
                                         aplicaci&oacute;n el siguiente fichero
                        </p>
                        
                                      
                        <div class="informalexample">
                                           
                           
                                           <pre class="programlisting">&lt;nombre &gt;-conf.php</pre>
                                         </div>
                        
                                      
                        <p>Donde &lt;nombre&gt; es el nombre del proyecto definido en
                                         el fichero <span class="emphasis"><em>build.properties</em></span></p>
                                    
                     </li>
                  </ol>
               </div>
               
                       
               <p><span class="bold"><strong>CASO 2: BBDD ya creada en un SGBD,
                                mediante ingenier&iacute;a inversa.</strong></span></p>
               
                       
               <p>Partimos de un punto mas pr&oacute;ximo a la realidad como es la
                          incorporaci&oacute;n de un ORM a un entorno ya existente, por lo que
                          evidentemente tenemos tambi&eacute;n la BBDD creada y funcionando. El proceso
                          es bastante similar, solo difiere algunos puntos.
               </p>
               
                       
               <div class="orderedlist">
                  <ol class="orderedlist" type="1">
                     <li class="listitem">
                                    
                        <p>En primer lugar hemos de generar el fichero
                                       <span class="emphasis"><em>build.properties</em></span> el cual tendr&aacute; el siguiente
                                       c&oacute;digo dentro
                        </p>
                        
                                    
                        <div class="informalexample">
                                         
                           
                                         <pre class="programlisting">propel.project = &lt;nombre&gt;

# The Propel driver to use for generating SQL, etc.
propel.database = &lt;driver&gt;

# This must be a PDO DSN
propel.database.url = &lt;driver&gt;:dbname=&lt;nombre&gt;
propel.database.user = &lt;user&gt;
# propel.database.password =&lt;pwd&gt;</pre>
                                       </div>
                                  
                     </li>
                     <li class="listitem">
                                    
                        <p>Una vez tenemos el fichero build.properties se hace la
                                       llamada a Propel para que nos genere el fichero XML con la
                                       estructura de la BBDD esta vez haremos uso del par&aacute;metro
                                       &#8220;reverse&#8221;.
                        </p>
                        
                                    
                        <div class="informalexample">
                                         
                           
                                         <pre class="programlisting">propel-gen reverse</pre>
                                       </div>
                        
                                    
                        <p>Con esto obtendremos un fichero
                                       <span class="emphasis"><em>schema.xml</em></span> que contendr&aacute; la especificaci&oacute;n de
                                       la BBDD en XML.
                        </p>
                                  
                     </li>
                     <li class="listitem">
                                    
                        <p>Ahora como en el caso 1, nos queda hacer la llamada a Propel
                                       para que nos genere las clases correspondientes, esto es pasandole
                                       el par&aacute;metro &#8220;om&#8221;, Object Model generador.
                        </p>
                        
                                    
                        <div class="informalexample">
                                         
                           
                                         <pre class="programlisting">propel-gen om</pre>
                                       </div>
                        
                                    
                        <p>Recordemos que por cada tabla en la BBDD, Propel crea tres
                                       clases de PHP:
                        </p>
                        
                                    
                        <div class="itemizedlist">
                           <ul class="itemizedlist" type="disc">
                              <li class="listitem">
                                                 
                                 <p>una clase modelo.</p>
                                               
                              </li>
                              <li class="listitem">
                                                 
                                 <p>una clase Peer.</p>
                                               
                              </li>
                              <li class="listitem">
                                                 
                                 <p>una clase Consulta.</p>
                                               
                              </li>
                           </ul>
                        </div>
                                  
                     </li>
                     <li class="listitem">
                                    
                        <p>Llegados a este punto, los pasos a seguir son los mismos que
                                       las que tenemos en el caso anterior, a partir del punto
                                       1.d.
                        </p>
                                  
                     </li>
                  </ol>
               </div>
               
                       
               <p>Llegados a este punto, ya tenemos la estructura de clases para
                          trabajar con Propel en nuestra aplicaci&oacute;n, ahora tendremos que incluir
                          la llamada correspondiente.
               </p>
               
                       
               <p>Mediante esta llamada se incluye la librer&iacute;a de propel, y adem&aacute;s
                          inicializamos las clases particulares para que nuestra aplicaci&oacute;n haga
                          uso de ella.
               </p>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">// Include the main Propel script
require_once 'propel/Propel.php';

// Initialize Propel with the runtime configuration
Propel::init("/&lt;proyecto&gt;/build/conf/bookstore-conf.php");

// Add the generated 'classes' directory to the include path
set_include_path("/&lt;proyecto&gt;//build/classes" . PATH_SEPARATOR . get_include_path());</pre>
                          </div>
                     
            </div>
            
                  
            <div class="section" title="5.3.2.4. Uso desde gvHidra">
               <div class="titlepage">
                  <div>
                     <div>
                        <h4 class="title"><a name="d4e4444"></a>5.3.2.4. Uso desde gvHidra
                        </h4>
                     </div>
                  </div>
               </div>
                       
               
                       
               <p>Vamos ha realizar un ejemplo.</p>
               
                       
               <p>Disponemos de una tabla, TrecjatReclamante, que tiene la
                          siguiente descripci&oacute;n:
               </p>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">CREATE TABLE trecjat_reclamante
(
  nif character varying(15) NOT NULL,
  nombre character varying(25),
  apellido1 character varying(50),
  apellido2 character varying(50),
  direccion character varying(100),
  poblacion character varying(100),
  provincia character varying(100),
  cp character varying(10),
  email character varying(25),
  fax character varying(10),
  telefono character varying(10),
  movil character varying(10),
  nombreempresa character varying(50),
  pais character varying(30) DEFAULT 'Espa&ntilde;a'::character varying,
  CONSTRAINT pk_reclamante PRIMARY KEY (nif)
)
WITH (OIDS=TRUE);
ALTER TABLE trecjat_reclamante OWNER TO recjat;
GRANT ALL ON TABLE trecjat_reclamante TO recjat;
GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE trecjat_reclamante TO rrecjat;</pre>
                          </div>
               
                       
               <p>Partiendo de esta descripci&oacute;n de la tabla, Propel genera las
                          siguientes clases (s&oacute;lo mostramos las mas relevantes):
               </p>
               
                       <span class="bold"><strong>dbgvhidra/build/classes/dbgvhidra/TrecjatReclamante.php</strong></span>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">



&lt;?php


/**
 * Skeleton subclass for representing a row from the 'trecjat_reclamante' table.
 *
 * 
 *
 * You should add additional methods to this class to meet the
 * application requirements.  This class will only be generated as
 * long as it does not already exist in the output directory.
 *
 * @package    propel.generator.dbgvhidra
 */
class TrecjatReclamante extends BaseTrecjatReclamante {

} // TrecjatReclamante

</pre>
                          </div>
               
                       <span class="bold"><strong>dbgvhidra/build/classes/dbgvhidra/TrecjatReclamanteQuery.php</strong></span>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">

&lt;?php


/**
 * Skeleton subclass for performing query and update operations on the 'trecjat_reclamante' table.
 *
 * 
 *
 * You should add additional methods to this class to meet the
 * application requirements.  This class will only be generated as
 * long as it does not already exist in the output directory.
 *
 * @package    propel.generator.dbgvhidra
 */
class TrecjatReclamanteQuery extends BaseTrecjatReclamanteQuery {

} // TrecjatReclamanteQuery
</pre>
                          </div>
               
                       <span class="bold"><strong>dbgvhidra/build/classes/dbgvhidra/om/BaseTrecjatReclamante.php</strong></span>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">
&lt;?php

/**
 * Base class that represents a row from the 'trecjat_reclamante' table.
 *
 * 
 *
 * @package    propel.generator.dbgvhidra.om
 */
abstract class BaseTrecjatReclamante extends BaseObject  implements Persistent
{

	/**
	 * Peer class name
	 */
  const PEER = 'TrecjatReclamantePeer';

	/**
	 * The Peer class.
	 * Instance provides a convenient way of calling static methods on a class
	 * that calling code may not be able to identify.
	 * @var        TrecjatReclamantePeer
	 */
	protected static $peer;

	/**
	 * The value for the nif field.
	 * @var        string
	 */
	protected $nif;

	/**
	 * The value for the nombre field.
	 * @var        string
	 */
	protected $nombre;

	/**
	 * The value for the apellido1 field.
	 * @var        string
	 */
	protected $apellido1;

	/**
	 * The value for the apellido2 field.
	 * @var        string
	 */
	protected $apellido2;

	/**
	 * The value for the direccion field.
	 * @var        string
	 */
	protected $direccion;

	/**
	 * The value for the poblacion field.
	 * @var        string
	 */
	protected $poblacion;

...

	/**
	 * Flag to prevent endless save loop, if this object is referenced
	 * by another object which falls in this transaction.
	 * @var        boolean
	 */
	protected $alreadyInSave = false;

	/**
	 * Flag to prevent endless validation loop, if this object is referenced
	 * by another object which falls in this transaction.
	 * @var        boolean
	 */
	protected $alreadyInValidation = false;

	/**
	 * Applies default values to this object.
	 * This method should be called from the object's constructor (or
	 * equivalent initialization method).
	 * @see        __construct()
	 */
	public function applyDefaultValues()
	{
		$this-&gt;pais = 'Espa&ntilde;a';
	}

	/**
	 * Initializes internal state of BaseTrecjatReclamante object.
	 * @see        applyDefaults()
	 */
	public function __construct()
	{
		parent::__construct();
		$this-&gt;applyDefaultValues();
	}

/*Setters AND Getters*/
...
/*END Setters AND Getters*/

	/**
	 * Indicates whether the columns in this object are only set to default values.
	 *
	 * This method can be used in conjunction with isModified() to indicate whether an object is both
	 * modified _and_ has some values set which are non-default.
	 *
	 * @return     boolean Whether the columns in this object are only been set with default values.
	 */
	public function hasOnlyDefaultValues()
	{
			if ($this-&gt;pais !== 'Espa&ntilde;a') {
				return false;
			}

		// otherwise, everything was equal, so return TRUE
		return true;
	} // hasOnlyDefaultValues()

	/**
	 * Hydrates (populates) the object variables with values from the database resultset.
	 *
	 * An offset (0-based "start column") is specified so that objects can be hydrated
	 * with a subset of the columns in the resultset rows.  This is needed, for example,
	 * for results of JOIN queries where the resultset row includes columns from two or
	 * more tables.
	 *
	 * @param      array $row The row returned by PDOStatement-&gt;fetch(PDO::FETCH_NUM)
	 * @param      int $startcol 0-based offset column which indicates which restultset column to start with.
	 * @param      boolean $rehydrate Whether this object is being re-hydrated from the database.
	 * @return     int next starting column
	 * @throws     PropelException  - Any caught Exception will be rewrapped as a PropelException.
	 */
	public function hydrate($row, $startcol = 0, $rehydrate = false)
	{
		try {

			$this-&gt;nif = ($row[$startcol + 0] !== null) ? (string) $row[$startcol + 0] : null;
			$this-&gt;nombre = ($row[$startcol + 1] !== null) ? (string) $row[$startcol + 1] : null;
			$this-&gt;apellido1 = ($row[$startcol + 2] !== null) ? (string) $row[$startcol + 2] : null;
			$this-&gt;apellido2 = ($row[$startcol + 3] !== null) ? (string) $row[$startcol + 3] : null;
			$this-&gt;direccion = ($row[$startcol + 4] !== null) ? (string) $row[$startcol + 4] : null;
			$this-&gt;poblacion = ($row[$startcol + 5] !== null) ? (string) $row[$startcol + 5] : null;
			$this-&gt;provincia = ($row[$startcol + 6] !== null) ? (string) $row[$startcol + 6] : null;
			$this-&gt;cp = ($row[$startcol + 7] !== null) ? (string) $row[$startcol + 7] : null;
			$this-&gt;email = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
			$this-&gt;fax = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null;
			$this-&gt;telefono = ($row[$startcol + 10] !== null) ? (string) $row[$startcol + 10] : null;
			$this-&gt;movil = ($row[$startcol + 11] !== null) ? (string) $row[$startcol + 11] : null;
			$this-&gt;nombreempresa = ($row[$startcol + 12] !== null) ? (string) $row[$startcol + 12] : null;
			$this-&gt;pais = ($row[$startcol + 13] !== null) ? (string) $row[$startcol + 13] : null;
			$this-&gt;resetModified();

			$this-&gt;setNew(false);

			if ($rehydrate) {
				$this-&gt;ensureConsistency();
			}

			return $startcol + 14; // 14 = TrecjatReclamantePeer::NUM_COLUMNS - TrecjatReclamantePeer::NUM_LAZY_LOAD_COLUMNS).

		} catch (Exception $e) {
			throw new PropelException("Error populating TrecjatReclamante object", $e);
		}
	}

	/**
	 * Checks and repairs the internal consistency of the object.
	 *
	 * This method is executed after an already-instantiated object is re-hydrated
	 * from the database.  It exists to check any foreign keys to make sure that
	 * the objects related to the current object are correct based on foreign key.
	 *
	 * You can override this method in the stub class, but you should always invoke
	 * the base method from the overridden method (i.e. parent::ensureConsistency()),
	 * in case your model changes.
	 *
	 * @throws     PropelException
	 */
	public function ensureConsistency()
	{

	} // ensureConsistency

	/**
	 * Reloads this object from datastore based on primary key and (optionally) resets all associated objects.
	 *
	 * This will only work if the object has been saved and has a valid primary key set.
	 *
	 * @param      boolean $deep (optional) Whether to also de-associated any related objects.
	 * @param      PropelPDO $con (optional) The PropelPDO connection to use.
	 * @return     void
	 * @throws     PropelException - if this object is deleted, unsaved or doesn't have pk match in db
	 */
	public function reload($deep = false, PropelPDO $con = null)
	{
		if ($this-&gt;isDeleted()) {
			throw new PropelException("Cannot reload a deleted object.");
		}

		if ($this-&gt;isNew()) {
			throw new PropelException("Cannot reload an unsaved object.");
		}

		if ($con === null) {
			$con = Propel::getConnection(TrecjatReclamantePeer::DATABASE_NAME, Propel::CONNECTION_READ);
		}

		// We don't need to alter the object instance pool; we're just modifying this instance
		// already in the pool.

		$stmt = TrecjatReclamantePeer::doSelectStmt($this-&gt;buildPkeyCriteria(), $con);
		$row = $stmt-&gt;fetch(PDO::FETCH_NUM);
		$stmt-&gt;closeCursor();
		if (!$row) {
			throw new PropelException('Cannot find matching row in the database to reload object values.');
		}
		$this-&gt;hydrate($row, 0, true); // rehydrate

		if ($deep) {  // also de-associate any related objects?

		} // if (deep)
	}

	/**
	 * Removes this object from datastore and sets delete attribute.
	 *
	 * @param      PropelPDO $con
	 * @return     void
	 * @throws     PropelException
	 * @see        BaseObject::setDeleted()
	 * @see        BaseObject::isDeleted()
	 */
	public function delete(PropelPDO $con = null)
	{
		if ($this-&gt;isDeleted()) {
			throw new PropelException("This object has already been deleted.");
		}

		if ($con === null) {
			$con = Propel::getConnection(TrecjatReclamantePeer::DATABASE_NAME, Propel::CONNECTION_WRITE);
		}
		
		$con-&gt;beginTransaction();
		try {
			$ret = $this-&gt;preDelete($con);
			if ($ret) {
				TrecjatReclamanteQuery::create()
					-&gt;filterByPrimaryKey($this-&gt;getPrimaryKey())
					-&gt;delete($con);
				$this-&gt;postDelete($con);
				$con-&gt;commit();
				$this-&gt;setDeleted(true);
			} else {
				$con-&gt;commit();
			}
		} catch (PropelException $e) {
			$con-&gt;rollBack();
			throw $e;
		}
	}

	/**
	 * Persists this object to the database.
	 *
	 * If the object is new, it inserts it; otherwise an update is performed.
	 * All modified related objects will also be persisted in the doSave()
	 * method.  This method wraps all precipitate database operations in a
	 * single transaction.
	 *
	 * @param      PropelPDO $con
	 * @return     int The number of rows affected by this insert/update and any referring fk objects' save() operations.
	 * @throws     PropelException
	 * @see        doSave()
	 */
	public function save(PropelPDO $con = null)
	{
		if ($this-&gt;isDeleted()) {
			throw new PropelException("You cannot save an object that has been deleted.");
		}

		if ($con === null) {
			$con = Propel::getConnection(TrecjatReclamantePeer::DATABASE_NAME, Propel::CONNECTION_WRITE);
		}
		
		$con-&gt;beginTransaction();
		$isInsert = $this-&gt;isNew();
		try {
			$ret = $this-&gt;preSave($con);
			if ($isInsert) {
				$ret = $ret AND $this-&gt;preInsert($con);
			} else {
				$ret = $ret AND $this-&gt;preUpdate($con);
			}
			if ($ret) {
				$affectedRows = $this-&gt;doSave($con);
				if ($isInsert) {
					$this-&gt;postInsert($con);
				} else {
					$this-&gt;postUpdate($con);
				}
				$this-&gt;postSave($con);
				TrecjatReclamantePeer::addInstanceToPool($this);
			} else {
				$affectedRows = 0;
			}
			$con-&gt;commit();
			return $affectedRows;
		} catch (PropelException $e) {
			$con-&gt;rollBack();
			throw $e;
		}
	}
...

} // BaseTrecjatReclamante
          
          </pre>
                          </div>
               
                       
               <p>Ahora vamos a ver como enganchar la clase que implementamos en
                          gvHidra, gesReclamante, con la generada por el Propel,
                          TRectjatReclamante:
               </p>
               
                       
               <div class="informalexample">
                            
                  
                            <pre class="programlisting">&lt;?php
/* ORM */
// Include the main Propel script 
<span class="bold"><strong>require_once 'C:\wamp\bin\php\php5.2.6\PEAR\propel\Propel.php';</strong></span>

// Initialize Propel with the runtime configuration
<span class="bold"><strong>Propel::init("C:\wamp\www\dbgvhidra\build\conf\dbgvhidra-conf.php");</strong></span>

// Add the generated 'classes' directory to the include path
<span class="bold"><strong>set_include_path("C:\wamp\www\dbgvhidra\build\classes" . PATH_SEPARATOR . get_include_path());</strong></span>

class gesReclamante extends gvHidraForm 
{
    public function __construct() 
    {
       parent::__construct();
    }//Fin de constructor

    public function preBuscar($objDatos) 
    {
        return 0;
    }

    public function postBuscar($objDatos) 
    {
        <span class="bold"><strong>$a_reclamantes = TrecjatReclamanteQuery::create()-&gt;find();</strong></span>
        $res = array(); 
        foreach($a_reclamantes as $reclamante) 
        {
                $row = array();
                $row['NIF'] = <span class="bold"><strong>$reclamante-&gt;getNif();</strong></span>
                $row['Nombre'] = <span class="bold"><strong>$reclamante-&gt;getNombre();</strong></span>
                $row['Apellido1'] = <span class="bold"><strong>$reclamante-&gt;getApellido1();</strong></span>
                $row['Apellido2'] = <span class="bold"><strong>$reclamante-&gt;getApellido2();</strong></span>
                array_push($res,$row);
        }
        $objDatos-&gt;setAllTuplas($res);
        return 0;  
    }

    public function preModificar($objDatos) 
    {
        $data = $objDatos-&gt;getAllTuplas(); 
        if(count($data[0])&gt;0) 
        {
            foreach($data as $row) 
            {
                <span class="bold"><strong>$reclamante = TrecjatReclamanteQuery::create()-&gt;findPK($row['NIF']);</strong></span>
                <span class="bold"><strong>$reclamante-&gt;setNombre($row['Nombre']);
                $reclamante-&gt;setApellido1($row['Apellido1']);
                $reclamante-&gt;setApellido2($row['Apellido2']);     
                $reclamante-&gt;setProvincia($row['Provincia']);
                $reclamante-&gt;save();</strong></span>
            }              
        }
        return 0;  
    }
  
    public function preBorrar($objDatos) 
    {
        $data = $objDatos-&gt;getAllTuplas();
        if(count($data[0])&gt;0) 
        {
            foreach($data as $row)
                <span class="bold"><strong>$reclamantes = TrecjatReclamanteQuery::create()-&gt;findPK($row['NIF']);</strong></span>
                <span class="bold"><strong>$reclamantes -&gt; delete();</strong></span>
        }
      
        return 0;  
    }
  
    public function preEditar($objDatos) 
    {
        $this-&gt;dataSelected = $objDatos-&gt;getAllTuplas();
        return 0;
    }
    
    public function postEditar($objDatos) 
    {  
        <span class="bold"><strong>$data = $this-&gt;dataSelected;</strong></span>
        $res = array(); 
        if(count($data[0])&gt;0) 
        {
           foreach($data as $row) 
           {
                <span class="bold"><strong>$reclamante = TrecjatReclamanteQuery::create()-&gt;findPK($row['NIF']);</strong></span>                            
                $row = array();
                $row['NIF'] = $reclamante-&gt;getNif();
                $row['Nombre'] = $reclamante-&gt;getNombre();
                $row['Apellido1'] = $reclamante-&gt;getApellido1();
                $row['Apellido2'] = $reclamante-&gt;getApellido2();
                $row['Provincia'] = $reclamante-&gt;getProvincia();
                array_push($res,$row);
            }
         }
         $objDatos-&gt;setAllTuplas($res);
         return 0;  
    }  
    
    public function preInsertar($objDatos) 
    {
        $data = $objDatos-&gt;getAllTuplas();      
        if(count($data[0])&gt;0) 
        {
            foreach($data as $row) 
            {  
               <span class="bold"><strong>$reclamante = new TrecjatReclamante();</strong></span>
               <span class="bold"><strong>$reclamante-&gt;setNif($row['NIF']);
               $reclamante-&gt;setNombre($row['Nombre']);
               $reclamante-&gt;setApellido1($row['Apellido1']);
               $reclamante-&gt;setApellido2($row['Apellido2']);     
               $reclamante-&gt;setProvincia($row['Provincia']);
               $reclamante-&gt;save();</strong></span>
            }              
        }
        return 0;  
    }
}
?&gt;</pre>
                          </div>
                     
            </div>
                
         </div>
           
      </div>
      <div class="navfooter">
         <hr>
         <table width="100%" summary="Navigation footer">
            <tr>
               <td width="40%" align="left"><a accesskey="p" href="ch05s02.html">Anterior</a>&nbsp;
               </td>
               <td width="20%" align="center"><a accesskey="u" href="ch05.html">Subir</a></td>
               <td width="40%" align="right">&nbsp;<a accesskey="n" href="ch06.html">Siguiente</a></td>
            </tr>
            <tr>
               <td width="40%" align="left" valign="top">5.2. Web Services&nbsp;</td>
               <td width="20%" align="center"><a accesskey="h" href="indice.html">Inicio</a></td>
               <td width="40%" align="right" valign="top">&nbsp;Cap&iacute;tulo 6. Seguridad</td>
            </tr>
         </table>
      </div>
   </body>
</html>